package com.scraper.articles.web.scrapers;

import com.scraper.articles.domain.Article;
import com.scraper.articles.domain.filter.ArticleFilter;
import com.scraper.articles.scanner.Scanner;
import com.scraper.articles.seekingalpha.SeekingAlphaScanner;
import com.scraper.articles.stockcharts.StockChartsScanner;
import com.scraper.articles.web.dao.ArticleDao;
import com.scraper.articles.web.model.Request;
import com.scraper.articles.web.model.SourceEnum;
import com.scraper.articles.web.scrapers.exception.FileSaveException;
import com.scraper.articles.web.scrapers.exception.ImageSaveException;
import com.scraper.articles.web.transfrormer.ArticleTransformer;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class RequestsProcessor {

    public static final String JPG_FILE_EXTENSION = ".jpg";

    @Autowired
    private ArticleDao articleDao;

    @Autowired
    private ArticleTransformer articleTransformer;

    @Autowired
    private ImageValidator imageValidator;

    @Value("${articles.text.location}")
    private String textLocation;

    @Value("${articles.image.location}")
    private String imagesLocation;

    @Transactional
    public void process(List<Request> requestList) {
        for (Request request : requestList) {
            processRequest(request);
        }
    }

    private void processRequest(Request request) {
        ArticleFilter filter = new ArticleFilter();
        filter.setSearchText(request.getSearchText());
        Scanner scanner = getScanner(request.getSource());
        List<Article> articlesList = scanner.scan(filter);
        articlesList = getNotProcessedArticles(articlesList);
        if (articlesList.isEmpty()) {
            return;
        }
        saveTexts(articlesList);
        downloadImages(articlesList);
        saveArticles(articlesList, request);
    }

    private List<Article> getNotProcessedArticles(List<Article> articlesList) {
        ArrayList<Article> articles = new ArrayList<>();
        for (Article article : articlesList) {
            if (!articleDao.isArticleProcessed(article.getUrl())) {
                articles.add(article);
            }
        }
        return articles;
    }

    private void saveTexts(List<Article> articlesList) {
        for (Article article : articlesList) {
            saveText(article);
        }
    }

    private void saveText(Article article) {
        PrintWriter out;
        PrintWriter htmlOut;
        String folder = textLocation + article.getFileName();
        new File(folder).mkdirs();

        String txtFileName = folder + "/" + article.getFileName() + ".txt";
        String htmlFileName = folder + "/" + article.getFileName() + ".html";
        try {
            out = new PrintWriter(txtFileName);
            out.println(article.getText());
            out.close();
            htmlOut = new PrintWriter(htmlFileName);
            htmlOut.println(article.getHtmlText());
            htmlOut.close();
        } catch (FileNotFoundException e) {
            throw new FileSaveException(e);
        }
    }

    private void downloadImages(List<Article> articlesList) {
        for (Article article : articlesList) {
            String folder = imagesLocation + article.getFileName();
            new File(folder).mkdirs();
            downloadArticleImages(article.getImages(), folder);
        }
    }

    private void downloadArticleImages(List<String> images, String folder) {
        for (String image : images) {
            downloadImage(image, folder);
        }
    }

    private void downloadImage(String image, String folder) {
        //Extract the name of the image from the src attribute
        if (!image.startsWith("http:")) {
            image = "http:" + image;
        }

        int indexname = image.lastIndexOf("/");
        if (indexname == image.length()) {
            image = image.substring(1, indexname);
        }
        indexname = image.lastIndexOf("/");
        String name = image.substring(indexname + 1, image.length());
        if (!imageValidator.validate(name)) {
            name = name + JPG_FILE_EXTENSION;
        }
        System.out.println(name);
        //Open a URL Stream
        URL url = null;
        try {
            url = new URL(image);
            InputStream in = url.openStream();
            if (!isFilenameValid(name)) {
                name = DigestUtils.md5Hex(name) + JPG_FILE_EXTENSION;
            }
            OutputStream out = new BufferedOutputStream(new FileOutputStream(folder + File.separator + name));
            for (int b; (b = in.read()) != -1; ) {
                out.write(b);
            }
            out.close();
            in.close();
        } catch (IOException e) {
            throw new ImageSaveException(e);
        }
    }

    public static boolean isFilenameValid(String file) {
        File f = new File(file);
        try {
            f.getCanonicalPath();
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    private void saveArticles(List<Article> articlesList, Request request) {
        articleDao.saveArticleList(articleTransformer.transform(articlesList, request.getSource(), request.getId()));
    }

    private Scanner getScanner(SourceEnum sourceEnum) {
        if (sourceEnum == null) {
            return null;
        }
        if (sourceEnum == SourceEnum.SEEKING_ALPHA) {
            return new SeekingAlphaScanner();
        } else if (sourceEnum == SourceEnum.STOCK_CHARTS) {
            return new StockChartsScanner();
        }
        return null;
    }

    @Component
    public static class ImageValidator {

        private Pattern pattern;
        private Matcher matcher;

        private static final String IMAGE_PATTERN =
                "([^\\s]+(\\.(?i)(jpg|png|gif|bmp))$)";

        public ImageValidator() {
            pattern = Pattern.compile(IMAGE_PATTERN);
        }

        /**
         * Validate image with regular expression
         *
         * @param image image for validation
         * @return true valid image, false invalid image
         */
        public boolean validate(final String image) {

            matcher = pattern.matcher(image);
            return matcher.matches();

        }
    }
}
